<?php
declare (strict_types=1);

namespace app\user\logic\user;

use app\admin\logic\system\Config as SystemConfig;
use app\common\exception\LogicException;
use app\common\helper\util\SnowFlake;
use app\common\logic\Payment;
use app\common\logic\Verify;
use app\common\model\payment\PaymentOrder as PO;
use app\common\model\payment\PaymentOrderItem as POI;
use app\common\model\ProductType;
use app\user\model\user\Balance as BalanceModel;
use app\user\model\user\Recharge;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Request;
use think\Model;

/**
 * 账户余额接口类
 * @method whereUserId(mixed $user_id)
 */
class Balance extends BalanceModel
{

    /**
     * 获取账户余额信息
     * Author   亓官雨树<lucky.max@foxmail.com>
     * Date 2020-04-21
     */
    public function basic(): array
    {
        // 用户余额
        $return['balance'] = $this->userBalance();
        // 支付方式
        $return['payway'] = app(Payment::class)->payway(false);
        // 充值配置
        try {
            $recharge = app(Recharge::class)->mhm()->order(['is_ios', 'balance'])->select()->toArray();
        } catch (DataNotFoundException|ModelNotFoundException|DbException $e) {
            $recharge = [];
        }
        // 过滤非iOS元素
        $pub = array_filter($recharge, function ($v) {
            return !$v['is_ios'];
        });
        $pub = array_map(function ($v) {
            unset($v['product_id']);
            return $v;
        }, $pub);
        // 过滤iOS元素
        $ios = array_filter($recharge, function ($v) {
            return $v['is_ios'];
        });
        $ios = array_map(function ($v) {
            $v['balance'] = $v['balance'] + $v['give_balance'];
            return $v;
        }, $ios);

        $ratio              = SystemConfig::get('recharge.config', ['ratio' => '1:1'], request()->mhm_id);
        $return['recharge'] = ['public' => $pub, 'ios' => array_values($ios), 'ratio' => $ratio['ratio']]; // 充值

        return $return;
    }

    /**
     * 获取用户余额
     * Author   亓官雨树<lucky.max@foxmail.com>
     * Date 2020-05-06
     */
    public function userBalance()
    {
        $balance = $this->scope('userBalance')->value('balance');

        return $balance ?: 0;
    }

    /**
     * 获取用户余额密码设置状态
     * Author   亓官雨树<lucky.max@foxmail.com>
     * Date 2021-10-14
     */
    public function userBalancePwdSetStatus(): int
    {
        return $this->scope('mid')->whereRaw('password is not null and password <> ""')->count();
    }

    /**
     * 扣除余额 - 包含购买课程和充值会员、积分，购买考试和商品
     * Author   亓官雨树<lucky.max@foxmail.com>
     * Date 2020-04-21
     */
    public function decreaseUserBalance($uid, $type, $num = 0, $rel_type = '', $rel_pk = '', $note = '')
    {
        $this->startTrans();
        try {
            $info = $this->where('user_id', $uid)->findOrEmpty();
            // 余额不足
            if ($info->isEmpty() || $info->balance < $num) throw new LogicException('当前用户余额不足');
            // 变更余额
            $info->balance -= $num;
            if (!$info->save()) throw new LogicException('操作失败');
            // $balance = $this->find($info->id);
            // 流水记录
            app(BalanceFlow::class)->updateFlow($uid, $type, $num, $info->balance, $rel_type, $rel_pk, $note);
            $this->commit();
        } catch (LogicException $e) {
            $this->rollback();
            throw new LogicException($e->getMessage());
        }

        return $info;
    }

    /**
     * 充值余额、收入提现
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-06-09
     * $mid 用户ID
     * $type 变动类型
     * $num 变动的金额
     * $rel_type 关联的表
     * $rel_pk   关联的表主键值
     * $note 备注
     *
     * @return Balance|array|Model
     */
    public function increaseUserBalance($mid, $type, $num = 0, $rel_type = '', $rel_pk = '', $note = '')
    {
        $this->startTrans();
        try {
            $balance = $this->where('user_id', $mid)->findOrEmpty();
            if ($balance->isEmpty()) {
                // 添加余额
                $balance          = new Balance;
                $balance->user_id = $mid;
                $balance->balance = $num;
            } else {
                // 变更余额
                $balance->balance += $num;
            }
            if (!$balance->save()) throw new LogicException('操作失败');
            // 流水记录
            app(BalanceFlow::class)->updateFlow($mid, $type, $num, $balance->balance, $rel_type, $rel_pk, $note);
            $this->commit();
        } catch (LogicException $e) {
            $this->rollback();
            throw new LogicException($e->getMessage());
        }
        return $balance;
    }

    /**
     * 余额充值
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-05-25
     */
    public function rechargeOrder($param)
    {
        // 验证数据
        $this->getValidate()->scene('recharge_order')->failException()->check($param);
        if ($param['recharge_id']) {
            // 获取支付金额配置
            $recharge_conf          = app(Recharge::class)->findOrFail($param['recharge_id']);
            $data['product_id']     = $recharge_conf->id;
            $data['payment']        = $recharge_conf->price;
            $data['original_price'] = $recharge_conf->balance + $recharge_conf->give_balance;
        } else {
            $ratio_conf = SystemConfig::get('recharge.config', ['ratio' => '1:1'], request()->mhm_id);
            [$a, $b] = explode(':', $ratio_conf['ratio']);
            $payment = round($a * $param['balance'] / $b, 2);
            if (abs($param['payment'] - $payment) > 0.02) {
                throw new LogicException('参数错误', '金额计算错误');
            }
            $data['product_id']     = 0;
            $data['payment']        = $param['payment'];
            $data['original_price'] = $param['balance'];
        }

        return $this->createOrder($data);
    }

    /**
     * 创建订单方法
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-06-08
     */
    protected function createOrder($data)
    {
        $from = strtolower(request()->device_type);
        $this->startTrans();
        try {
            // 用户ID
            $mid = request()->mid;
            // 操作频繁处理
            $update_time = app(POI::class)
                ->where('user_id', $mid)
                ->where('product_type_id', ProductType::getProductIdByKey('balance'))
                ->order('order_no', 'DESC')
                ->value('update_time');

            if (($update_time + 5) > Request::time()) throw new LogicException('操作过于频繁,请稍后再试');
            //
            $PD = app(SnowFlake::class)->createPD();
            $PI = app(SnowFlake::class)->createPI();
            // 主订单
            $order = app(PO::class)::create([
                'order_no'   => $PD,
                'user_id'    => $mid,
                'from'       => $from,
                'mhm_id'     => request()->user->mhm_id,
                'payment'    => $data['payment'],
                'status'     => '10',
                'close_time' => time() + 30 * 60,
            ]);

            if ($order->isEmpty()) throw new LogicException('订单生成失败', '主订单生成失败');
            // 子订单
            $order_item = app(POI::class)::create([
                'order_no'        => $PD,
                'order_sub_no'    => $PI,
                'user_id'         => $mid,
                'from'            => $from,
                'mhm_id'          => request()->user->mhm_id,
                'product_id'      => $data['product_id'],
                'product_type_id' => ProductType::getProductIdByKey('balance'),
                'payment'         => $data['payment'],
                'original_price'  => $data['original_price'],
            ]);
            if ($order_item->isEmpty()) throw new LogicException('订单生成失败', '子订单生成失败');

            $this->commit();
        } catch (LogicException $e) {
            $this->rollback();
            throw new LogicException($e->getMessage());
        }
        unset($order->id);
        return $order;
    }

    /**
     * 支付密码 - 验证手机号
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-12-22
     */
    public function pwdStat(): array
    {
        //
        $user_id = request()->mid;
        //
        $data = $this->whereUserId($user_id)->findOrEmpty();

        $exist = $data->isEmpty() ? 0 : ($data->password ? 1 : 0);

        return ['exist' => $exist];
    }

    /**
     * 支付密码 - 验证手机号
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-12-22
     */
    public function pwdVerify($param)
    {
        // 验证数据
        $this->getValidate()->scene('pwd_verify')->failException()->check($param);
        //
        $code  = $param['code'];
        $phone = $param['phone'];
        //
        app(Verify::class)->check($phone, 'bpwd', $code);
        cache('bpwd_' . request()->mid, 1, 300);
    }

    /**
     * 支付密码 - 验证原密码
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-12-22
     */
    public function pwdBefore($param)
    {
        // 验证数据
        $this->getValidate()->scene('pwd_before')->failException()->check($param);
        //
        $password = $param['password'];
        $user_id  = request()->mid;
        //
        $correct = $this->whereUserId($user_id)->wherePassword($password)->findOrEmpty();
        if ($correct->isEmpty()) throw new LogicException('原密码错误');
    }

    /**
     * 设置支付密码
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-12-22
     */
    public function setPwd($param)
    {
        $user_id = request()->mid;
        //
        $app = in_array(request()->device_type, ['h5', 'ios', 'android']);
        if ($app) {
            $this->validateSetPhone($param);
            if (request()->user->phone && !cache('bpwd_' . $user_id)) throw new LogicException('存在手机号，请验证');
        } else {
            $this->validateSetDefault($param);
        }
        //
        $password = $param['password'];
        //
        $exist = $this->whereUserId($user_id)->findOrEmpty();
        if ($exist->isEmpty()) {
            $re = $this::create([
                'user_id'  => $user_id,
                'password' => $password
            ]);
            if ($re->isEmpty()) throw new LogicException('密码设置失败');
        } else {
            //
            if ($exist->password) throw new LogicException('已设置密码');
            //
            $exist->password = $password;
            if (!$exist->save()) throw new LogicException('密码设置失败');
        }
    }

    /**
     * 设置支付密码 - APP
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-12-22
     */
    public function validateSetPhone($param)
    {
        // 验证数据
        $this->getValidate()->scene('pwd_set_phone')->failException()->check($param);
    }

    /**
     * 设置支付密码 - 非APP
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-12-22
     */
    public function validateSetDefault($param)
    {
        // 验证数据
        $this->getValidate()->scene('pwd_set_default')->failException()->check($param);
        //
        $phone = $param['phone'];
        $code  = $param['code'];
        //
        app(Verify::class)->check($phone, 'bpwd', $code);
    }

    /**
     * 修改支付密码
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-12-22
     */
    public function updatePwd($param)
    {
        //
        $app = in_array(request()->device_type, ['miniprogram', 'h5', 'ios', 'android']);
        if ($app) {
            $this->validateUpdateApp($param);
        } else {
            $this->validateUpdateDefault($param);
        }
        //
        $password = $param['password'];
        $user_id  = request()->mid;
        //
        $exist = $this->whereUserId($user_id)->findOrEmpty();
        if ($exist->isEmpty()) {
            $re = $this::create([
                'user_id'  => $user_id,
                'password' => $password
            ]);
            if ($re->isEmpty()) throw new LogicException('密码设置失败');
        } else {
            //
            $exist->password = $password;
            if (!$exist->save()) throw new LogicException('密码设置失败');
        }
    }

    /**
     * 修改支付密码验证 - APP
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-12-22
     */
    public function validateUpdateApp($param)
    {
        // 验证数据
        $this->getValidate()->scene('pwd_update_app')->failException()->check($param);
    }

    /**
     * 修改支付密码验证 - 非APP
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-12-22
     */
    public function validateUpdateDefault($param)
    {
        //
        $type = $param['verify_type'] ?? 0;
        if (1 == $type) {
            // 验证数据
            $this->getValidate()->scene('pwd_update_original')->failException()->check($param);
            //
            $password = $param['original_pwd'];
            $user_id  = request()->mid;
            //
            $correct = $this->whereUserId($user_id)->wherePassword($password)->findOrEmpty();
            if ($correct->isEmpty()) throw new LogicException('原密码错误');
        } else if (2 == $type) {
            // 验证数据
            $this->getValidate()->scene('pwd_update_phone')->failException()->check($param);
            //
            $phone = $param['phone'];
            $code  = $param['code'];
            //
            app(Verify::class)->check($phone, 'bpwd', $code);
        } else {
            throw new LogicException('参数错误');
        }
    }

}
